package top.hymsk.ecommerce.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.util.RandomUtil;
import org.apache.ibatis.annotations.Param;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import top.hymsk.ecommerce.dao.mapper.UserMapper;
import top.hymsk.ecommerce.pojo.DTO.StoreInfoDTO;
import top.hymsk.ecommerce.pojo.PO.mysql.UserPO;
import top.hymsk.ecommerce.pojo.response.GeneralResponse;
import top.hymsk.ecommerce.pojo.status.ResponseStatusCode;
import top.hymsk.ecommerce.service.UserService;
import top.hymsk.ecommerce.service.information.email.MailClient;
import top.hymsk.ecommerce.service.information.messages.MessageClient;
import top.hymsk.ecommerce.utils.CryptoUtils;
import top.hymsk.ecommerce.utils.JwtUtils;
import top.hymsk.ecommerce.utils.RegexMatchUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author 花于陌上开
 */
@Service
public class UserServiceImpl implements UserService {
    private final static int MAX_FETCH_TIMES = 3;  //一天最大获取次数
    private final static int FETCH_INTERVAL_TIME = 60;  //获取间隔时间，单位(秒)
    //初始化
    private final UserMapper userMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final MessageClient messageClient;
    private final MailClient mailClient;


    public UserServiceImpl(UserMapper userMapper, StringRedisTemplate stringRedisTemplate, MessageClient messageClient, MailClient mailClient) {
        this.userMapper = userMapper;
        this.stringRedisTemplate = stringRedisTemplate;
        this.messageClient = messageClient;
        this.mailClient = mailClient;
    }

    /**
     * 查询用户名是否存在
     *
     * @param username 用户名
     * @return 验证状态
     */
    @Override
    public GeneralResponse userNameIsExist(@Param("username") String username) {
        //验证用户是否存在
        //用户不存在
        if (userMapper.isUserExist(username, null, null) == 0) {
            return GeneralResponse.fail(ResponseStatusCode.USER_NOT_EXIST);
        }
        //用户存在
        return GeneralResponse.success();
    }

    /**
     * 进行图灵测试验证
     *
     * @param parameter 参数
     * @return (图片信息和)验证状态
     */
    @Override
    public GeneralResponse getVerificationTuring(@Param("parameter") String parameter) {
        //生成图形验证码
        CircleCaptcha circleCaptcha = CaptchaUtil.createCircleCaptcha(200, 76, 6, 400);
        //生成code
        circleCaptcha.createCode();
        //将验证码结果与用户信息放入Redis(有效期1分钟)
        stringRedisTemplate.opsForValue().set(String.format("%s:turing", parameter), circleCaptcha.getCode(), 60, TimeUnit.SECONDS);
        return GeneralResponse.success(circleCaptcha.getImageBase64());
    }

    /**
     * 获取验证码
     *
     * @param parameter        手机号/邮箱
     * @param verificationCode 图灵测试验证码
     * @return 验证状态
     */
    @Override
    public GeneralResponse sendVerificationCode(String parameter, String verificationCode) {
        ///验证图形验证码
        String code = stringRedisTemplate.opsForValue().get(String.format("%s:turing", parameter));
        if (code == null) {
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_CODE_EXPIRED_OR_NOT_EXIST);
        } else if (!Objects.equals(verificationCode, code)) {
            stringRedisTemplate.delete(String.format("%s:turing", parameter));
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_FAILED);
        }
        String newCode;
        if (RegexMatchUtils.isPhoneNumber(parameter)) {
            ///有重置密码同样需要获取验证码
//            //验证手机号是否存在，若有任意信息存在拒绝发送
//            if (userMapper.isUserExist(null, parameter, null) != 0) {
//                return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
//            }
            try {
                newCode = createVerificationCode(parameter);
            } catch (Exception e) {
                if ("请求次数过多".equals(e.getMessage())) {
                    return GeneralResponse.fail(ResponseStatusCode.REQUESTS_TOO_FREQUENT);
                } else {
                    return GeneralResponse.fail(ResponseStatusCode.REQUEST_RESTRICTED);
                }
            }
            //发送手机验证码
            Integer statusCode = messageClient.sendMessageWithAllNumberCode(parameter, newCode);
            System.out.println(statusCode);
            if (statusCode != 200) {
                return GeneralResponse.fail(ResponseStatusCode.MESSAGE_CLIENT_SEND_FAILED);
            }
            return GeneralResponse.success();
        } else if (RegexMatchUtils.isEmail(parameter)) {
            ///有重置密码同样需要获取验证码
//            //验证邮箱是否存在，若有任意信息存在拒绝发送
//            if (userMapper.isUserExist(null, null, parameter) != 0) {
//                return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
//            }
            try {
                newCode = createVerificationCode(parameter);
            } catch (Exception e) {
                if ("请求次数过多".equals(e.getMessage())) {
                    return GeneralResponse.fail(ResponseStatusCode.REQUESTS_TOO_FREQUENT);
                } else {
                    return GeneralResponse.fail(ResponseStatusCode.REQUEST_RESTRICTED);
                }
            }
            //发送邮箱验证码
            try {
                mailClient.sendMessageWithAllNumberCode(parameter, newCode);
            } catch (Exception e) {
                return GeneralResponse.fail(ResponseStatusCode.EMAIL_CLIENT_SEND_FAILED);
            }
            return GeneralResponse.success();
        }
        return GeneralResponse.fail(ResponseStatusCode.PARAMETER_INPUT_ERROR);


    }

    /**
     * 进行注册
     *
     * @param userName         用户名
     * @param parameter        手机号/邮箱
     * @param verificationCode 验证码
     * @param password         密码
     * @return 注册状态
     */
    @Override
    public GeneralResponse userRegister(String userName, String parameter, String verificationCode, String password) {
        //验证用户名与手机号是否存在(前面已经验证过用户名和手机号，事实上若正常请求只有输入验证码之后修改用户名可能出现)，若有任意信息存在拒绝发送
        if (userMapper.isUserExist(userName, parameter, parameter) != 0) {
            return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
        }
        //验证码是否存在或超时(15分钟)/超出验证次数(3次)，验证失败拒绝注册
        String phoneCodeTimesString = stringRedisTemplate.opsForValue().get(String.format("codetimes:%s", parameter));
        if (phoneCodeTimesString == null) {  //验证码不存在
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_CODE_EXPIRED_OR_NOT_EXIST);
        } else if (Integer.parseInt(phoneCodeTimesString) >= 3) {  //验证次数过多
            return GeneralResponse.fail(ResponseStatusCode.EXCESSIVE_VERIFICATION);
        }
        //验证验证码是否正确
        stringRedisTemplate.opsForValue().increment(String.format("codetimes:%s", parameter));
        String code = stringRedisTemplate.opsForValue().get(String.format("code:%s", parameter));
        if (!Objects.equals(verificationCode, code)) {
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_FAILED);
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行注册
        try {
            int status;
            if (RegexMatchUtils.isPhoneNumber(parameter)) {
                status = userMapper.registerByPhoneNumber(userName, encryptedPassword, parameter);
            } else if (RegexMatchUtils.isEmail(parameter)) {
                status = userMapper.registerByEmail(userName, encryptedPassword, parameter);
            } else {
                return GeneralResponse.fail(ResponseStatusCode.PARAMETER_INPUT_ERROR);
            }
            if (status == 0) {
                return GeneralResponse.fail(ResponseStatusCode.UNKNOWN_ERROR);
            }
        } catch (org.springframework.dao.DuplicateKeyException e) {
            return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
        } catch (Exception e) {
            return GeneralResponse.fail(ResponseStatusCode.SERVER_ERROR);
        }
        return GeneralResponse.success();
    }

    /**
     * 重置密码
     *
     * @param parameter        手机号/邮箱
     * @param verificationCode 验证码
     * @param password         密码
     * @return 重置密码状态
     */
    @Override
    public GeneralResponse userResetPassword(String parameter, String verificationCode, String password) {
        //验证用户名与手机号是否存在，若不存在信息存在拒绝发送
        if (userMapper.isUserExist(null, parameter, parameter) == 0) {
            return GeneralResponse.fail(ResponseStatusCode.PARAMETER_INPUT_ERROR);
        }
        //验证码是否存在或超时(15分钟)/超出验证次数(3次)，验证失败拒绝注册
        String phoneCodeTimesString = stringRedisTemplate.opsForValue().get(String.format("codetimes:%s", parameter));
        if (phoneCodeTimesString == null) {  //验证码不存在
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_CODE_EXPIRED_OR_NOT_EXIST);
        } else if (Integer.parseInt(phoneCodeTimesString) >= 3) {  //验证次数过多
            return GeneralResponse.fail(ResponseStatusCode.EXCESSIVE_VERIFICATION);
        }
        //验证验证码是否正确
        stringRedisTemplate.opsForValue().increment(String.format("codetimes:%s", parameter));
        String code = stringRedisTemplate.opsForValue().get(String.format("code:%s", parameter));
        if (!Objects.equals(verificationCode, code)) {
            return GeneralResponse.fail(ResponseStatusCode.VERIFICATION_FAILED);
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行修改密码
        try {
            int status;
            if (RegexMatchUtils.isPhoneNumber(parameter)) {
                status = userMapper.updatePasswordByPhoneNumber(parameter, encryptedPassword);
            } else if (RegexMatchUtils.isEmail(parameter)) {
                status = userMapper.updatePasswordByEmail(parameter, encryptedPassword);
            } else {
                return GeneralResponse.fail(ResponseStatusCode.PARAMETER_INPUT_ERROR);
            }
            if (status == 0) {
                return GeneralResponse.fail(ResponseStatusCode.UNKNOWN_ERROR);
            }
        } catch (Exception e) {
            return GeneralResponse.fail(ResponseStatusCode.SERVER_ERROR);
        }
        return GeneralResponse.success();
    }

    /**
     * 登录
     *
     * @param parameter 用户名/手机号/邮箱
     * @param password  密码
     * @return 登录状态
     */
    @Override
    public GeneralResponse login(String parameter, String password) {
        UserPO user;
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        if (RegexMatchUtils.isPhoneNumber(parameter)) { //匹配为电话号码
            user = userMapper.selectUserByPhoneNumber(parameter);
        } else if (RegexMatchUtils.isEmail(parameter)) { //匹配为邮箱
            user = userMapper.selectByEmail(parameter);
        } else { //匹配为用户名
            user = userMapper.selectUserByName(parameter);
        }
        if (user == null) {
            return GeneralResponse.fail(ResponseStatusCode.USER_NOT_EXIST);
        }
        if (Objects.equals(encryptedPassword, user.getPassword())) {
            String token=JwtUtils.createJwt(user.getUserId(), userMapper.getStoreIdByUserId(user.getUserId()));
            return GeneralResponse.success(token);
        }
        return GeneralResponse.fail(ResponseStatusCode.ACCOUNT_AND_PASSWORD_NOT_MATCH);
    }

    /**
     * 注册为商户
     *
     * @param userId    用户名
     * @param storeName 店铺名
     * @return 返回状态信息
     */
    @Override
    public GeneralResponse joinSeller(Integer userId, String storeName) {
        //若登录状态正确，之前已经验证过用户id，只可能是店铺重名
        if (userMapper.ifStoreInfoRepetition(userId, storeName) == 1) {
            return GeneralResponse.fail(ResponseStatusCode.STORE_IS_EXIST);
        }
        userMapper.insertNewStore(userId, storeName);
        return GeneralResponse.success();
    }

    /**
     * 注册后门
     *
     * @param userName  用户名
     * @param parameter 手机号/邮箱
     * @param password  密码
     * @return 注册状态
     */
    public GeneralResponse doRegister(String userName, String parameter, String password) {
        if (userMapper.isUserExist(userName, parameter, parameter) != 0) {
            return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
        }
        //密码加密
        String encryptedPassword = CryptoUtils.encrypted(password);
        //进行注册
        try {
            int status;
            if (RegexMatchUtils.isPhoneNumber(parameter)) {
                status = userMapper.registerByPhoneNumber(userName, encryptedPassword, parameter);
            } else if (RegexMatchUtils.isEmail(parameter)) {
                status = userMapper.registerByEmail(userName, encryptedPassword, parameter);
            } else {
                return GeneralResponse.fail(ResponseStatusCode.PARAMETER_INPUT_ERROR);
            }
            if (status == 0) {
                return GeneralResponse.fail(ResponseStatusCode.UNKNOWN_ERROR);
            }
        } catch (org.springframework.dao.DuplicateKeyException e) {
            return GeneralResponse.fail(ResponseStatusCode.USER_IS_EXIST);
        } catch (Exception e) {
            return GeneralResponse.fail(ResponseStatusCode.SERVER_ERROR);
        }
        return GeneralResponse.success();
    }

    /**
     * 创建验证码并缓存在Redis中
     *
     * @param parameter 手机号/邮箱
     * @return 验证码
     */
    protected String createVerificationCode(String parameter) throws Exception {
        //检查Redis中验证码获取是否过于频繁，规定24小时内一个手机只能获取MAX_FETCH_TIMES次(间隔：FETCH_INTERVAL_TIME秒)，过于频繁则拒绝发送
        long timeout = stringRedisTemplate.getExpire(String.format("code:%s", parameter));
        if (timeout >= (15 * 60) - FETCH_INTERVAL_TIME) { // 验证码有效期>(15分钟-设置间隔时间)，请求过于频繁
            throw new Exception("请求过于频繁");
        }
        String timesString = stringRedisTemplate.opsForValue().get(String.format("%s:times", parameter));
        if (timesString == null) {  //手机号第一次获取验证码
            stringRedisTemplate.opsForValue().set(String.format("%s:times", parameter), String.valueOf(1), 24 * 60 * 60, TimeUnit.SECONDS);
        } else {
            int times = Integer.parseInt(timesString);
            if (times < MAX_FETCH_TIMES) {  //一天之内未获取到MAX_FETCH_TIMES，可以发送
                stringRedisTemplate.opsForValue().set(String.format("%s:times", parameter), String.valueOf(++times), 24 * 60 * 60, TimeUnit.SECONDS);
            } else {  //获取次数大于等于MAX_FETCH_TIMES
                throw new Exception("请求次数过多");
            }
        }
        //生成验证码
        String newCode = RandomUtil.randomNumbers(6);
        //缓存验证码及存在时间
        stringRedisTemplate.opsForValue().set(String.format("code:%s", parameter), newCode, 15 * 60, TimeUnit.SECONDS);
        //缓存验证码验证次数及存在时间
        stringRedisTemplate.opsForValue().set(String.format("codetimes:%s", parameter), String.valueOf(0), 15 * 60, TimeUnit.SECONDS);
        return newCode;
    }

    /**
     * 获取店铺信息
     *
     * @param userId  用户id
     * @param storeId 店铺id
     * @return 返回状态信息，需要重新登录才能生效
     */
    @Override
    public GeneralResponse sellerInfo(Integer userId, Integer storeId){
        StoreInfoDTO storeInfo=userMapper.getStoreInfo(userId,storeId);
        if (storeInfo==null){
            return GeneralResponse.fail(ResponseStatusCode.USER_OR_AUTHENTICATION_ERROR);
        }
        return GeneralResponse.success(storeInfo);
    }

    /**
     * 获取用户信息
     *
     * @param userId 用户id
     * @return 返回状态信息，需要重新登录才能生效
     */
    @Override
    public GeneralResponse buyerInfo(Integer userId) {
        return GeneralResponse.success(userMapper.selectUserById(userId).getUsername());
    }


}
